﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Windows.Input;
using ScrumFactory.Composition;
using ScrumFactory.Composition.ViewModel;
using ScrumFactory.Services;
using ScrumFactory.Windows.Helpers;
using System.Linq;
using ScrumFactory.Composition.View;

namespace ScrumFactory.Projects.ViewModel {

    public enum BurndownPlanningCurves : short {
        CURRENT_PLANNING,
        PROPOSAL_PLANNING,
        FIRST_PLANNING
    }

    
    [Export(typeof(BurndownViewModel))]
    public class BurndownViewModel : BasePanelViewModel, IViewModel, INotifyPropertyChanged {

        private IBacklogService backlogService;
        private IBackgroundExecutor executor;
        private IEventAggregator aggregator;

        private ICollection<BurndownLeftHoursByDay> actualHours;
        private ICollection<BurndownLeftHoursByDay> actualHoursAhead;
        private ICollection<BurndownLeftHoursByDay> plannedHours;

        private BurndownPlanningCurves planningCurveMode;

        private string deadlinePositionLabel;

        public Project Project { get; private set; }

        [ImportingConstructor()]
        public BurndownViewModel(
            [Import] IBacklogService backlogService,
            [Import] IBackgroundExecutor backgroundExecutor,
            [Import] IEventAggregator eventAggregator) {

            this.backlogService = backlogService;
            this.executor = backgroundExecutor;
            this.aggregator = eventAggregator;

            NeedRefresh = false;

            OnLoadCommand = new DelegateCommand(() => {if (NeedRefresh) LoadData();});

            ChangePlanningCurveCommand = new DelegateCommand<string>(
                mode => {
                    PlanningCurveMode = (BurndownPlanningCurves) Enum.Parse(typeof(BurndownPlanningCurves), mode);
                });

            aggregator.Subscribe<Project>(ScrumFactoryEvent.ViewProjectDetails, AskForRefresh);

            aggregator.Subscribe<Project>(ScrumFactoryEvent.BurndownShouldRefresh, AskForRefresh);
            aggregator.Subscribe<ICollection<Sprint>>(ScrumFactoryEvent.SprintsDateChanged, o => { AskForRefresh(); });
            aggregator.Subscribe<BacklogItem[]>(ScrumFactoryEvent.BacklogItemsChanged, b => { AskForRefresh(); });

        }

        public bool IsVisible {
            get {
                return View.IsVisible;
            }            
        }

        private void AskForRefresh() {
            AskForRefresh(Project);
        }

        private void AskForRefresh(Project project) {
            Project = project;
            if (IsVisible)
                LoadData();
            else
                NeedRefresh = true;            
        }
      

        private void LoadData() {                        
            IsLoadingData = true;
            executor.StartBackgroundTask<ICollection<BurndownLeftHoursByDay>>(LoadCurveData, OnDataLoaded);
        }

        private ICollection<BurndownLeftHoursByDay> LoadCurveData() {
            if (Project == null)
                return new List<BurndownLeftHoursByDay>();
            if(PlanningCurveMode==BurndownPlanningCurves.FIRST_PLANNING)
                return backlogService.GetBurndownHoursByDayFirstPlanning(Project.ProjectUId);
            if(PlanningCurveMode==BurndownPlanningCurves.PROPOSAL_PLANNING)
                return backlogService.GetBurndownHoursByDayProposalPlanning(Project.ProjectUId);

            return backlogService.GetBurndownHoursByDay(Project.ProjectUId);

        }

        private void OnDataLoaded(ICollection<BurndownLeftHoursByDay> leftHoursByDay) {
            IsLoadingData = false;
            NeedRefresh = false;
            ActualHours = leftHoursByDay.Where(h => h.LeftHoursMetric==LeftHoursMetrics.LEFT_HOURS).ToArray();
            ActualHoursAhead = leftHoursByDay.Where(h => h.LeftHoursMetric == LeftHoursMetrics.LEFT_HOURS_AHEAD).ToArray();
            PlannedHours = leftHoursByDay.Where(h => h.LeftHoursMetric == LeftHoursMetrics.PLANNING).ToArray();

            OnPropertyChanged("WalkedPct");
            OnPropertyChanged("DeadlinePosition");

        }

        

        #region IBurndownViewModel Members

        public decimal WalkedPct {
            get {
                if (PlannedHours == null || PlannedHours.Count == 0)
                    return 0;
                if (ActualHours == null || ActualHours.Count == 0)
                    return 0;

                decimal plannedTotalHours = 0;

                // if PROPOSAL PLANNING takes the first total hours where it is more than zero
                if (PlanningCurveMode == BurndownPlanningCurves.PROPOSAL_PLANNING) {
                    BurndownLeftHoursByDay planned = PlannedHours.FirstOrDefault(h => h.TotalHours > 0);
                    if (planned != null)
                        plannedTotalHours = planned.TotalHours;
                }

                // if CURRENT PLANNING takes the max totla hours until now
                if(PlanningCurveMode==BurndownPlanningCurves.CURRENT_PLANNING)
                    plannedTotalHours = PlannedHours.Max(d => d.TotalHours);

                // if FIRST PLANNING takes the max total hours until sprint 1
                if (PlanningCurveMode == BurndownPlanningCurves.FIRST_PLANNING)
                    plannedTotalHours = PlannedHours.Where(h => h.SprintNumber == 1).Max(h => h.TotalHours);


                BurndownLeftHoursByDay actual = ActualHours.FirstOrDefault(d => d.Date.Equals(DateTime.Today));
                if (actual == null)
                    actual = ActualHours.Last();
                if (actual == null)
                    return 0;                

                if (actual.TotalHours == 0)
                    return 100;

                if (plannedTotalHours == 0)
                    return -100;


                decimal done = plannedTotalHours - actual.TotalHours;
                decimal walked = done / plannedTotalHours * 100;


                return walked;
            }
        }

        private decimal deadLinePosition;

        private bool deadlineAhead;

        public decimal DeadlinePosition {
            get {
                if (PlannedHours == null || PlannedHours.Count == 0)
                    return 0;
                if (ActualHours == null || ActualHours.Count == 0)
                    return 0;

                BurndownLeftHoursByDay planned = PlannedHours.FirstOrDefault(d => d.Date.Equals(DateTime.Today));
                if (planned == null)
                    planned = plannedHours.Last();
                BurndownLeftHoursByDay actual = ActualHours.FirstOrDefault(d => d.Date.Equals(DateTime.Today));
                if (actual == null)
                    actual = ActualHours.Last();
                if (actual == null || planned == null)
                    return 0;
                
                decimal position = planned.TotalHours - actual.TotalHours;

                if (position < 0) {
                    deadlineAhead = false;
                    DeadlinePositionLabel = Properties.Resources.hrs_behind;
                }
                else {
                    deadlineAhead = true;
                    DeadlinePositionLabel = Properties.Resources.hrs_ahead;
                }
                
                return Math.Abs(position);
            }
        }

        public string DeadlinePositionLabel {
            get {
                return deadlinePositionLabel;
            }
            private set {
                deadlinePositionLabel = value;
                OnPropertyChanged("DeadlinePositionLabel");
                OnPropertyChanged("DeadlinePositionStatus");
            }
        }

        public int DeadlinePositionStatus {
            get {                
                if (!deadlineAhead)
                    return (int)IndicatorStatus.OVER;
                return (int)IndicatorStatus.NORMAL;
            }
        }

        [Import(typeof(Burndown))]
        public IView View { get; set; }

        public BurndownPlanningCurves PlanningCurveMode {
            get {
                return planningCurveMode;
            }
            set {
                planningCurveMode = value;
                OnPropertyChanged("PlanningCurveMode");
                LoadData();
            }
        }
     
        public ICollection<BurndownLeftHoursByDay> PlannedHours {
            get {
                return plannedHours;
            }
            set {
                plannedHours = value;
                OnPropertyChanged("PlannedHours");
            }
        }

        public ICollection<BurndownLeftHoursByDay> ActualHours {
            get {
                return actualHours;
            }
            set {
                actualHours = value;
                OnPropertyChanged("ActualHours");
            }
        }

        public ICollection<BurndownLeftHoursByDay> ActualHoursAhead {
            get {
                return actualHoursAhead;
            }
            set {
                actualHoursAhead = value;
                OnPropertyChanged("ActualHoursAhead");
            }
        }

        public ICommand OnLoadCommand { get; private set; }

        public ICommand ChangePlanningCurveCommand { get; private set; }

        #endregion
    }
}
